using System;
using System.Collections.Generic;
using System.Text;
using System.Collections;
using System.Xml;

using SlimDX;

using miciv.Rendering;

namespace miciv.Geometry
{
    public class GeometryBase : IGeometry
    {
        public class Semantic<T>
        {
            protected T m_value;
            public T Value
            {
                get
                {
                    return this.m_value;
                }
                set
                {
                    this.m_value = value;
                }
            }

            public Semantic()
            {

            }
        }

        protected CoreContext m_coreContext;
        protected IRenderer m_renderer;
        protected IShape m_shape;
        protected List<IVertexFeeder> m_vertexFeeders;
        protected Hashtable m_htblSemantics;
        protected VertexDefinition m_vertexDefinition;
        protected Type m_targetRenderableType;
        protected Vector2 m_f2Size;
        protected int m_sLevelOfDetail;
        protected bool m_bAutoSize;
        protected string m_strShape;
        protected string m_strID;

        public GeometryBase(CoreContext _coreContext, XmlElement _xmlGeometry, Type _targetRenderableType)
        {
            this.m_coreContext = _coreContext;
            this.m_htblSemantics = new Hashtable();
            this.m_targetRenderableType = _targetRenderableType;
            this.m_f2Size = new Vector2(0.0f, 0.0f);
            this.m_sLevelOfDetail = 1;
            Initialize(_xmlGeometry);
        }

        protected void Initialize(XmlElement _xmlGeometry)
        {
            CoreManager coreManager = this.m_coreContext.CoreManager;
            this.ID = _xmlGeometry.Attributes["id"].Value;
            // vertex definition
            this.VertexDefinition = this.m_coreContext.GetData<VertexDefinitionManager>("VertexDefinitionManager").Get(_xmlGeometry.Attributes["vertexdefinitionid"].Value);
            if (null == this.VertexDefinition)
            {
                throw new Exception(string.Format("{1} : unknown {0} vertex definition.", _xmlGeometry.Attributes["id"].Value, _xmlGeometry.OwnerDocument.BaseURI));
            }
            // renderer
            this.Renderer = this.m_coreContext.GetData<RendererManager>("RendererManager").GetByVertexDefinition(_xmlGeometry.Attributes["vertexdefinitionid"].Value, this.m_targetRenderableType);
            if (null == this.Renderer)
            {
                throw new Exception(string.Format("{1} : could not found renderer for {0} vertex definition.", _xmlGeometry.Attributes["vertexdefinitionid"].Value, _xmlGeometry.OwnerDocument.BaseURI));
            }
            // sahpe, level of detail
            this.m_strShape = _xmlGeometry.Attributes["shape"].Value;
            this.m_sLevelOfDetail = int.Parse(_xmlGeometry.Attributes["levelofdetail"].Value);
            // semantics
            XmlNodeList lstSemantics = _xmlGeometry.SelectNodes("semantics/semantic");
            foreach (XmlElement xmlSemantic in lstSemantics)
            {
                string semanticID = xmlSemantic.Attributes["id"].Value;
                if (false != this.m_htblSemantics.ContainsKey(semanticID))
                {
                    throw new Exception(string.Format("{1} : {0} semantic has already been declared.", semanticID, xmlSemantic.OwnerDocument.BaseURI));
                }
                switch (xmlSemantic.Attributes["type"].Value)
                {
                    case "texture":
                    {
                        Semantic<TextureManager.ManagedTexture> semantic = new Semantic<TextureManager.ManagedTexture>();
                        semantic.Value = this.m_coreContext.GetData<TextureManager>("TextureManager").GetTexture(xmlSemantic.Attributes["value"].Value);
                        this.m_htblSemantics.Add(semanticID, semantic);
                        break;
                    }
                    case "rgba":
                    {
                        Semantic<Color4> semantic = new Semantic<Color4>();
                        char[] separator = { ' ' };
                        string[] rgba = xmlSemantic.Attributes["value"].Value.Split(separator, StringSplitOptions.RemoveEmptyEntries);
                        if (4 != rgba.Length)
                        {
                            throw new Exception(string.Format("{1} : {0} rgba semantic badly formatted.", semanticID, xmlSemantic.OwnerDocument.BaseURI));
                        }
                        semantic.Value = new Color4(
                            float.Parse(rgba[3]),
                            float.Parse(rgba[0]),
                            float.Parse(rgba[1]),
                            float.Parse(rgba[2]));
                        this.m_htblSemantics.Add(semanticID, semantic);
                        break;
                    }
                }
            }
            // size (HACKY : read auto size state after semantics in case we need to use one them)
            XmlElement xmlSize = _xmlGeometry.SelectSingleNode("size") as XmlElement;
            this.m_f2Size.X = float.Parse(coreManager.GetXmlAttributeValue(xmlSize, "x", "0.0"));
            this.m_f2Size.Y = float.Parse(coreManager.GetXmlAttributeValue(xmlSize, "y", "0.0"));
            this.AutoSize = bool.Parse(coreManager.GetXmlAttributeValue(xmlSize, "autosize", "false"));

            this.BuildShapeVertexFeeders();
        }

        protected bool BuildShapeVertexFeeders()
        {
            bool bResult = false;
            this.Shape = this.m_coreContext.GetData<ShapeManager>("ShapeManager").Create(this.m_strShape);
            if (null != this.Shape)
            {
                this.Shape.VertexDefinition = this.VertexDefinition;
                this.Shape.Size = new Vector3(this.m_f2Size, 0.0f);
                this.Shape.LevelOfDetail = 1;
                bResult = this.Shape.Build(null);
                if (false != bResult)
                {
                    if (null != this.m_vertexFeeders)
                    {
                        this.Renderer.ReleaseFeeders(this.m_vertexFeeders);
                    }
                    this.m_vertexFeeders = this.Renderer.CreateFeeders(this.Shape);
                }
            }
            return ((null != this.Shape)
                && (false != bResult)
                && (null != this.m_vertexFeeders)
                && (0 <  this.m_vertexFeeders.Count));
        }

        #region IGeometry Membres

        virtual public string ID
        {
            get
            {
                return this.m_strID;
            }
            set
            {
                this.m_strID = value;
            }
        }

        virtual public IRenderer Renderer
        {
            get
            {
                return this.m_renderer;
            }
            set
            {
                this.m_renderer = value;
            }
        }

        virtual public IShape Shape
        {
            get
            {
                return this.m_shape;
            }
            set
            {
                this.m_shape = value;
            }
        }

        virtual public List<IVertexFeeder> VertexFeeders
        {
            get
            {
                return this.m_vertexFeeders;
            }
            set
            {
                this.m_vertexFeeders = value;
            }
        }

        virtual public VertexDefinition VertexDefinition
        {
            get
            {
                return this.m_vertexDefinition;
            }
            set
            {
                this.m_vertexDefinition = value;
            }
        }

        virtual public Vector2 Size
        {
            get
            {
                return this.m_f2Size;
            }
            set
            {
                if (false == this.m_bAutoSize)
                {
                    this.m_f2Size = value;
                }
            }
        }

        virtual public bool AutoSize
        {
            get
            {
                return this.m_bAutoSize;
            }
            set
            {
                if (value != this.m_bAutoSize)
                {
                    this.m_bAutoSize = value;
                    if (false != this.m_bAutoSize)
                    {
                        TextureManager.ManagedTexture mTex = GetSemantic<TextureManager.ManagedTexture>("AlbedoTexture");
                        if (null != mTex)
                        {
                            this.m_bAutoSize = false;
                            this.Size = new Vector2(mTex.Info.Width, mTex.Info.Height);
                            this.m_bAutoSize = true;
                        }
                    }
                }
            }
        }

        virtual public bool SetSemantic<T>(string _strSemanticID, T _data)
        {
            if (false == this.m_htblSemantics.ContainsKey(_strSemanticID))
            {
                Semantic<T> semantic = new Semantic<T>();
                semantic.Value = _data;
                this.m_htblSemantics.Add(_strSemanticID, semantic);
                return true;
            }
            return false;
        }

        virtual public T GetSemantic<T>(string _strSemanticID)
        {
            if (false != this.m_htblSemantics.ContainsKey(_strSemanticID))
            {
                Semantic<T> semantic = this.m_htblSemantics[_strSemanticID] as Semantic<T>;
                if (null != semantic)
                {
                    return semantic.Value;
                }
            }
            return default(T);
        }

        virtual public void Release()
        {
            if (null != this.VertexFeeders)
            {
                this.Renderer.ReleaseFeeders(this.VertexFeeders);
            }
        }

        #endregion
    }
}
